package user.functions;

import java.util.ArrayList;

import user.data.Data;
import graphmanip.Graph;
import graphmanip.collapsing.*;
import guiobjects.Observation;
import commonfunctions.Matrix;


/**
 * This algorithm is created by the user, and produces the two-dimensional boolean array indicating the path of an object.
 * 
 * @author Trevor Terris
 *
 */
public class PruningAlgorithm 
{
	/**
	 * Takes a two dimensional array of doubles, the graph of probabilities. Returns a two-dimensional array of booleans. (i, j) is true if j is the first observation after i that corresponds to teh same object.
	 * 
	 * @param probabilityGraph
	 * @return
	 */
	public static Matrix prune(Matrix probabilityGraph, ArrayList<Observation> observations)
	{	
		int algoNum = Data.algoNum;
    	
    	switch(algoNum)
    	{
    	case 0:
	    		return followOnePathAlgo(probabilityGraph);
	
    	case 1:
				return greedyTrackletAlgo(probabilityGraph);
				
    	case 2: 
    			return matrixPruning(probabilityGraph, observations);
    			
    	default: 
    			return new Matrix(0);
    	}
	}
	private static Matrix followOnePathAlgo(Matrix probabilityGraph) {
		double lowerBound = 0.0;
		int size = probabilityGraph.length();
		Matrix output = new Matrix(size);
		
		for (int k = 0; k < size; k++)
		{
			for (int j = 0; j < size; j++)
			{
				output.set(false, k, j);
			}
		}
		
		for (int k = 0; k < size; k++)
		{
			int thisFrame = k;
			while (hasProbabilities(probabilityGraph.singleRow(thisFrame), lowerBound))
			{
				int maxIndex = 0;
				for (int j = 0; j < size; j++)
				{
					if (probabilityGraph.get(thisFrame, j) > probabilityGraph.get(thisFrame, maxIndex))
						maxIndex = j;
				}
				if (probabilityGraph.get(thisFrame,maxIndex) > lowerBound)
					output.set(true, thisFrame, maxIndex);
				for (int j = 0; j < size; j++)
				{
					probabilityGraph.set(0.0, j, maxIndex);
					probabilityGraph.set(0.0, thisFrame, j);
				}
				thisFrame = maxIndex;
			}
		}
		
		return output;
	}
	private static Matrix greedyTrackletAlgo(Matrix probabilityGraph) {
		Matrix output = new Matrix(probabilityGraph.length());
		
		double max;
		
		do 
		{
			max = 0.0;
			int maxStart = 0;
			int maxEnd = 0;
			for (int i = 0; i < output.length(); i++)
			{
				for (int j = i + 1; j < output.length(); j++)
				{
					if (probabilityGraph.get(i, j) > max)
					{
						max = probabilityGraph.get(i, j);
						maxStart = i;
						maxEnd = j;
					}
				}
			}
			for (int i = 0; i < output.length(); i++)
			{
				probabilityGraph.set(0.0, maxStart, i);
				probabilityGraph.set(0.0, i, maxEnd);
			}
			output.set(true, maxStart, maxEnd);
		}
		while (max >= 0.0);
		
		return output;
	}
	private static boolean hasProbabilities(double[] probs, double lowerBound)
	{
		for (int k = 0; k < probs.length; k++)
		{
			if (probs[k] > lowerBound)
				return true;
		}
		return false;
	}
	private static Matrix matrixPruning(Matrix probs, ArrayList<Observation> obs)
	{
		Graph g = new Graph(probs, obs);
		int iterationNum = 1;
		int oldSize = obs.size() + 1;
		int newSize = probs.length();
		while (oldSize > newSize && !g.isFinished())
		{
			if (iterationNum == 1)
				g.collapseGraph(new EvenSmarterHungarianCollapsing(obs), false);
			else
				g.collapseGraph(new EvenSmarterHungarianCollapsing(), true);
			g.collapseGraph(new ClumpingCollapsing(), true);
			System.out.println("Iteration number " + iterationNum + " completed.");
			iterationNum++;
			oldSize = newSize;
			newSize = g.size();
		}
		g.collapseGraph(new ClumpingCollapsing(), true);
		if (!g.isFinished())
		{
			System.out.println("Unable to complete graph. Final graph size: " + (g.size() - g.unconnectedNodes().size()) + " nodes.");
		}
		else
			System.out.println("Graph is finished.");
		return g.trackedGraph();
	}
}
